package com.longtxt.powerband

import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanRecord
import android.bluetooth.le.ScanResult
import android.content.Context
import android.os.ParcelUuid
import android.util.Log
import android.util.SparseArray
import androidx.core.util.containsKey
import com.band.smartband.core.ble.GattStatus
import com.band.smartband.core.ble.HciStatus
import com.google.gson.Gson
import com.jieli.jl_bt_ota.constant.StateCode
import com.jieli.jl_bt_ota.interfaces.BtEventCallback
import com.jieli.jl_bt_ota.interfaces.IActionCallback
import com.jieli.jl_bt_ota.interfaces.IUpgradeCallback
import com.jieli.jl_bt_ota.model.base.BaseError
import com.jieli.jl_bt_ota.model.response.TargetInfoResponse
import com.jieli.jl_rcsp.util.JL_Log
import com.longtxt.powerband.core.BLEConstants
import com.longtxt.powerband.core.BleCommonErrorCallback
import com.longtxt.powerband.core.BleDevice
import com.longtxt.powerband.core.BlePeripheral
import com.longtxt.powerband.core.BlePeripheralCallback
import com.longtxt.powerband.core.BleScanner
import com.longtxt.powerband.core.SimpleBluetoothLeDeviceStore
import com.longtxt.powerband.jieLi.JLWatchManager
import com.longtxt.powerband.models.BandAlarmData
import com.longtxt.powerband.models.BandConfig
import com.longtxt.powerband.models.BandFeatures
import com.longtxt.powerband.models.BandHeartSettingData
import com.longtxt.powerband.models.BandSleepDetail
import com.longtxt.powerband.models.BandTimeModel
import com.longtxt.powerband.models.BandUserInfo
import com.longtxt.powerband.models.BandVersion
import com.longtxt.powerband.models.BandWakeDetail
import com.longtxt.powerband.models.BandWeatherData
import com.longtxt.powerband.models.BaseResponse
import com.longtxt.powerband.models.CustomThemeData
import com.longtxt.powerband.models.DeviceItem
import com.longtxt.powerband.models.HeartExamineData
import com.longtxt.powerband.models.SportDailyData
import com.longtxt.powerband.models.WatchNotifyData
import com.longtxt.powerband.ota.OtaManager
import com.longtxt.powerband.util.tinyInt
import com.longtxt.powerband.util.toByte
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.charset.StandardCharsets
import java.util.UUID
import kotlin.math.ceil

typealias BleResponseCallback = (String) -> Unit

@SuppressLint("StaticFieldLeak")
object PbManager {
    private var bleScanner: BleScanner? = null
    private const val SERVICE_FIRST = "00008505-0000-1000-8000-00805F9B34FB"
    private const val SERVICE_SECOND = "00003802-0000-1000-8000-00805F9B34FB"
    private var mDeviceStore = SimpleBluetoothLeDeviceStore()
    var bluetoothAdapter: BluetoothAdapter? = null
    val callBackStack = mutableMapOf<String, BleResponseCallback>()
    var progressCallback: ((Int, Int, String) -> Unit)? = null
    var reScanCallback: ((BluetoothDevice) -> Unit)? = null
    var bleStatusCallback: ((String) -> Unit)? = null

    private var mContext: Context? = null

    private var selectedDevice: BluetoothDevice? = null
    val gsonInstance = Gson()

    var basicBandConfig = BandConfig.Companion.default

    private var watchThemeManager: WatchThemeManager? = null
    private var otaManager: OtaManager? = null

    private var jlCustomSuffix = "WATCH5"
    private var jlDialMarketSuffix = "WATCH6"
    private var sdkState = false
    private var maxOtaRetry = 3

    var otaReconnectAddress = ""


    val isRoundWatch
        get() = basicBandConfig.watchType == 4 || basicBandConfig.watchType == 5
    var isJLieWatch = false

    val isDeviceConnected: Boolean
        get() = blePeripheral != null
    val isSdkInitialized: Boolean get() = sdkState

    val isInOta: Boolean
        get() = otaManager?.isOtaProcess == true

    lateinit var baseFileUrl: File

    private const val SDK_VERSION = "2025.07.25-1.0.9"

    fun initScanner(context: Context, callback: (String) -> Unit) {
        mContext = context
        val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
        baseFileUrl = context.filesDir
        bluetoothAdapter = btManager?.adapter
        if (bluetoothAdapter == null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Bluetooth not available",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        otaManager = OtaManager(context)
        val ret = otaManager?.registerBluetoothCallback(otaEventCallback)
        JL_Log.d("PbManager", "register result $ret")
        bleScanner = BleScanner(
            bluetoothAdapter!!,
            leScanCallback
        ) {
            handleBleScanResult()
        }
        val baseResponse = BaseResponse<String>(
            code = 1,
            message = "SDK initialized successfully, version: $SDK_VERSION",
            data = null
        )
        callback(
            gsonInstance.toJson(baseResponse)
        )
        sdkState = true
    }

    fun setDesiredMtuSize(mtu: Int) {
        BlePeripheral.MAX_MTU = mtu
    }

    fun setUpBleStatusListener(callback: (String) -> Unit) {
        bleStatusCallback = callback
    }

    private val otaEventCallback: BtEventCallback = object : BtEventCallback() {
        override fun onConnection(p0: BluetoothDevice?, p1: Int) {
            val callback = callBackStack["initOtaModule"]
            if (p1 == StateCode.CONNECTION_OK) {
                if (otaManager?.isOTA == true) {
                    val baseResponse = BaseResponse<String>(
                        code = 0,
                        message = "Init OTA SDK Success",
                        data = null
                    )
                    callback?.invoke(gsonInstance.toJson(baseResponse))
                    return
                }
            } else {
                JL_Log.d("PbManager", "Connection failed with status: $p1")
                val baseResponse = BaseResponse<String>(
                    code = 1,
                    message = "Init OTA SDK failed",
                    data = null
                )
                callback?.invoke(gsonInstance.toJson(baseResponse))
            }
        }
    }

    fun initOtaModule(callback: (String) -> Unit) {
        otaManager?.isOtaProcess = true
        otaManager?.setUpBlePeripheral(blePeripheral!!)
        otaManager?.initOTASdk()

        MainScope().launch {
            delay(500)
            otaManager?.queryMandatoryUpdate(object : IActionCallback<TargetInfoResponse>{
                override fun onSuccess(p0: TargetInfoResponse?) {

                    val baseResponse = BaseResponse<String>(
                        code = -1,
                        message = "Init OTA SDK Success, need force update",
                        data = null
                    )
                    callback.invoke(gsonInstance.toJson(baseResponse))
                }

                override fun onError(p0: BaseError?) {
                    JL_Log.d("PbManager", p0?.message)
                    val baseResponse = BaseResponse<String>(
                        code = 1,
                        message = "Init OTA SDK Success",
                        data = null
                    )
                    callback.invoke(gsonInstance.toJson(baseResponse))
                }
            })
        }
    }

    fun deInitOtaModule(callback: (String) -> Unit) {
        otaManager?.isOtaProcess = false
        otaManager?.clearBlePeripheral()
        val baseResponse = BaseResponse<String>(
            code = 1,
            message = "OTA Module de-initialized successfully",
            data = null
        )
        callback(
            gsonInstance.toJson(baseResponse)
        )
    }


    fun scanForTargetDevice(mac: String, callback: (BluetoothDevice?) -> Unit) {
        if (maxOtaRetry <= 0) {
            val callback = callBackStack["performWatchOta"]
            if (callback != null) {
                val baseResponse = BaseResponse<String>(
                    code = 0,
                    message = "Max OTA retry limit reached",
                    data = null
                )
                callback(gsonInstance.toJson(baseResponse))
            }
            maxOtaRetry = 3
            return
        }
        if (bleScanner == null) {
            val callback = callBackStack["performWatchOta"]
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Bluetooth not available or SDK not initialized",
                data = null
            )
            callback?.invoke(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        mDeviceStore.clear()
        otaReconnectAddress = mac
        reScanCallback = callback
        scanResultTempArray.clear()
        bleScanner?.startScan(10)
    }

    fun startScan(timeOut: Int, callback: (String) -> Unit) {
        if (bleScanner == null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Bluetooth not available or SDK not initialized",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        scanResultTempArray.clear()
        callBackStack["startScan"] = callback
        bleScanner?.startScan(timeOut)
    }

    fun connectByMac(context: Context, mac: String, callback: (String) -> Unit) {
        if (blePeripheral != null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Already connected to a device",
                data = blePeripheral?.name ?: "Unknown"
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        selectedDevice = mDeviceStore.deviceByMac(mac)?.bleDevice
        if (selectedDevice != null) {
            callBackStack["connectBle"] = callback
            connectWithMac(context)
        } else {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Device not found",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
        }
    }

    fun disconnect(callback: (String) -> Unit) {
        callBackStack["disconnect"] = callback
        if (blePeripheral == null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Bluetooth not connected",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        blePeripheral?.disconnect()
    }

    private fun handleBleScanResult() {
        scanResultTempArray.values.forEach {
            if (it.scanRecord != null) {
                handleSearchResults(it.scanRecord!!, it.rssi, it.device)
            }
        }
        JL_Log.d("PbManager", "Scan results: ${scanResultTempArray.size} devices found")
        if (reScanCallback != null) {
            val device = mDeviceStore.deviceByMac(otaReconnectAddress)
            if (device != null) {
                reScanCallback?.invoke(device.bleDevice)
                reScanCallback = null
            } else {
                JL_Log.d("PBManager", "Device not found in scan results")
                maxOtaRetry -= 1
                bleScanner?.startScan(4)
            }
            return
        } else {
            Log.i("PbManager", "handleBleScanResult called ${mDeviceStore.deviceList}")
            val deviceList = mutableListOf<BleDevice>()
            mDeviceStore.deviceList.forEach {
                deviceList.add(BleDevice(it.name, it.mac, it.rssi))
            }
            val baseResponse = BaseResponse<List<BleDevice>>(
                code = 1,
                message = "未过滤设备数量${scanResultTempArray.size}",
                data = deviceList
            )
            callBackStack["startScan"]?.let {
                it(gsonInstance.toJson(baseResponse))
            }
        }
    }

    private var scanResultTempArray = HashMap<String, ScanResult>()

    private val leScanCallback: ScanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult?) {
            super.onScanResult(callbackType, result)
             result?.let {
                 if (it.rssi > -90) {
                     scanResultTempArray[it.device.address] = result
                 }
             }
        }
    }

    @SuppressLint("MissingPermission")
    private fun handleSearchResults(scanRecord: ScanRecord, rssi: Int, device: BluetoothDevice) {
        if (scanRecord.manufacturerSpecificData == null) {
            return
        }

        var macAddress =
            searchInManufacture(listOf(0x8505, 0x0105, 0x05D6), scanRecord.manufacturerSpecificData)
        if (macAddress == "" && scanRecord.serviceData != null) {
            macAddress =
                searchInService(listOf(SERVICE_FIRST, SERVICE_SECOND), scanRecord.serviceData)
        }
        val devName = device.name ?: scanRecord.deviceName
        if (macAddress != "") {
            val deviceItem = DeviceItem(
                devName ?: "Unknown",
                macAddress,
                rssi,
                device
            )
            mDeviceStore.addDevice(deviceItem)
        }
    }

    private fun searchInManufacture(
        checkList: List<Int>,
        checkData: SparseArray<ByteArray>
    ): String {
        for (flag in checkList) {
            if (checkData.containsKey(flag)) {
                checkData.get(flag)?.let { macData ->
                    if (macData.size > 5) {
                        if (flag == 0x05D6) {
                            val originalData = macData.copyOfRange(6, 12)
                            originalData.reverse()
                            return originalData.joinToString(separator = ":") {
                                String.format(
                                    "%02X",
                                    it
                                )
                            }
                        }
                        return macData.copyOfRange(0, 6)
                            .joinToString(separator = ":") { String.format("%02X", it) }
                    }
                }
            }
        }
        return ""
    }

    private fun searchInService(
        checkList: List<String>,
        checkData: Map<ParcelUuid, ByteArray>
    ): String {
        for (flag in checkList) {
            val parcelUuid = ParcelUuid(UUID.fromString(flag))
            if (checkData.containsKey(parcelUuid)) {
                checkData[parcelUuid]?.let { serviceD ->
                    if (serviceD.size > 5) {
                        return serviceD.copyOfRange(0, 6)
                            .joinToString(separator = ":") { String.format("%02X", it) }
                    }
                }
            }
        }
        return ""
    }

    private var blePeripheral: BlePeripheral? = null
    private var _mErrorCallback: BleCommonErrorCallback? = null

    private val blePeripheralCallback = object : BlePeripheralCallback {

        override fun onJLServiceFound() {
            isJLieWatch = true
        }
        override fun connecting(peripheral: BlePeripheral) {}

        override fun connected(peripheral: BlePeripheral) {}

        override fun connectFailed(peripheral: BlePeripheral, status: HciStatus) {
            val strSuccess = gsonInstance.toJson(
                BaseResponse<Any>(
                    0,
                    "Connect failed",
                    null
                )
            )
            callBackStack["connectBle"]?.invoke(strSuccess)
            bleStatusCallback?.invoke(strSuccess)

        }

        override fun disconnecting(peripheral: BlePeripheral) {}

        override fun disconnected(peripheral: BlePeripheral, status: HciStatus) {
            blePeripheral?.clearServicesCache()
            blePeripheral = null
            watchThemeManager = null
            otaManager?.clearBlePeripheral()
            JLWatchManager.shared.changeAuthState(false)
            val disStr = gsonInstance.toJson(
                BaseResponse<Any>(
                    code = 0,
                    message = "Disconnected",
                    data = null
                )
            )
            callBackStack["disconnect"]?.invoke(disStr)
            bleStatusCallback?.invoke(disStr)
        }

        override fun onMtuChanged(peripheral: BlePeripheral, mtu: Int, status: GattStatus) {
            // Here we treat the BLE is setup successfully
            if (isJLieWatch) {
                JLWatchManager.shared.setUpDevice(blePeripheral!!)
            } else {
                callBackStack["connectBle"]?.invoke(
                    gsonInstance.toJson(
                        BaseResponse<Any>(
                            1,
                            "Connect Success",
                            null
                        )
                    )
                )
            }

        }

        private fun commonSuccessResponse(msg: String, cmdName: String) {
            val baseResponse = BaseResponse<String>(
                code = 1,
                message = msg,
                data = "Success"
            )
            callBackStack[cmdName]?.invoke(gsonInstance.toJson(baseResponse))
        }

        override fun onReceiveResponse(jsonObject: ByteArray) {

            when (jsonObject.first()) {
                BLEConstants.COMMAND_BAND_CONFIG -> processBasicConfig(jsonObject)
                BLEConstants.COMMAND_BAND_VER -> processBandVersion(jsonObject)
                BLEConstants.COMMAND_SUPPORT_FUNC -> processSupportedFunc(jsonObject)
                BLEConstants.COMMAND_USER_INFO -> processUserInfo(jsonObject)
                BLEConstants.COMMAND_SPORT_DATA -> parseDailyData(jsonObject)
                BLEConstants.COMMAND_SLEEP_DATA -> parseSleepDetail(jsonObject)
                BLEConstants.COMMAND_BATTERY_SOC -> parseBatterySocData(jsonObject)
                BLEConstants.COMMAND_OS_PLATFORM -> {
                    commonSuccessResponse("Success", "setOSPlatform")
                }

                BLEConstants.COMMAND_LANGUAGE -> {
                    commonSuccessResponse("Success", "syncTimeAndLanguage")
                }

                BLEConstants.COMMAND_WEATHER -> {
                    commonSuccessResponse("Success", "syncWeatherToBand")
                }

                BLEConstants.COMMAND_SET_USER_INFO -> {
                    commonSuccessResponse("Success", "setUserInfo")
                }

                BLEConstants.COMMAND_ANTI_LOST -> {
                    commonSuccessResponse("Success", "setAntiLost")
                }

                BLEConstants.COMMAND_LIGHT_UP -> {
                    commonSuccessResponse("Success", "setGestureLightUp")
                }

                BLEConstants.COMMAND_SCREEN_SAVER -> {
                    commonSuccessResponse("Success", "setScreenSaver")
                }

                BLEConstants.COMMAND_ALARM -> {
                    commonSuccessResponse("Success", "setBandAlarm")
                }

                BLEConstants.COMMAND_HEART_RATE -> {
                    commonSuccessResponse("Success", "setHeartRateSetting")
                }

                BLEConstants.COMMAND_REJECT_CALL -> {
                    callBackStack["addCallStatusListener"]?.invoke(
                        gsonInstance.toJson(
                            BaseResponse<Boolean>(
                                1,
                                "Success",
                                jsonObject[1].tinyInt() == 2
                            )
                        )
                    )
                }

                BLEConstants.COMMAND_SCREEN_DATA -> {
                    watchThemeManager?.sendFilePacket(true)
                }

                BLEConstants.COMMAND_MARKET_DATA -> {
                    watchThemeManager?.sendFilePacket(false)
                }

                BLEConstants.COMMAND_EXAMINE_RESP -> parseHeartRateData(jsonObject)
                BLEConstants.COMMAND_FIND_PHONE -> {
                    commonSuccessResponse("Find phone success", "addFindPhoneListener")
                }

                BLEConstants.COMMAND_CAMERA_TO_BAND -> {
                    commonSuccessResponse("Send camera signal", "addShakeCameraListener")
                }

                BLEConstants.COMMAND_CAMERA_OPEN -> {
                    commonSuccessResponse("Open camera", "addShakeCameraOpenListener")
                }
//                BLEConstants.COMMAND_WATCH_LIGHT_UP -> _notifyData.value = BleNotification.LightUpSignal(jsonObject[1].tinyInt() == 1)
                else -> {}
            }

        }

        override fun onReceiveJLResponse(device: BluetoothDevice, rawData: ByteArray) {
            if (!JLWatchManager.shared.isAuthPass) {
                JLWatchManager.shared.notifyReceivedData(rawData)
            } else if (otaManager?.isOtaProcess == true) {
                otaManager?.onReceiveDeviceData(device, rawData)
            } else {
                JLWatchManager.shared.notifyReceivedData(rawData)
            }
        }

        override fun onConnectStatus(device: BluetoothDevice, status: Int) {
            if (otaManager?.isOtaProcess == true) {
                otaManager?.notifyConnectionState(device, status)
            } else {
                JLWatchManager.shared.notifyConnectionState(device, status)
            }
        }
    }


    fun connectWithMac(context: Context) {
        blePeripheral = BlePeripheral(
            context, selectedDevice!!, blePeripheralCallback,
            object : BleCommonErrorCallback {
                override fun onTimeout() {}

                override fun onError(error: String) {
                }
            })
        if (otaManager?.isOtaProcess == true) {
            blePeripheral!!.isInOta = true
        }
        blePeripheral?.connect()
    }

    fun getBasicConfig(callback: (String) -> Unit) {
        callBackStack["getBasicConfig"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_BAND_CONFIG))
    }

    fun getBandVersion(callback: (String) -> Unit) {
        callBackStack["readBandVersion"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_BAND_VER))
    }

    fun getSupportedFeatures(callback: (String) -> Unit) {
        callBackStack["getSupportedFeatures"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_SUPPORT_FUNC))
    }

    fun getUserInfo(callback: (String) -> Unit) {
        callBackStack["getUserInfo"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_USER_INFO))
    }

    fun startHealthExamine(callback: (String) -> Unit) {
        callBackStack["startHealthExamine"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_EXAMINE_REQ, 0x01))
    }

    fun getSportData(dayOffset: Int) {
        blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_SPORT_DATA,
                dayOffset.toByte()
            )
        )
    }

    fun getBatterySoc(callback: (String) -> Unit) {
        callBackStack["getBatterySoc"] = callback
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_BATTERY_SOC))
    }

    fun getSleepDetail(offSet: Int) {
        blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_SLEEP_DATA,
                offSet.toByte()
            )
        )
    }

    fun addSleepDataListener(callback: (String) -> Unit) {
        callBackStack["getSleepDetail"] = callback
    }

    fun addSportDataListener(callback: (String) -> Unit) {
        callBackStack["addSportDataListener"] = callback
    }

    fun addFindPhoneListener(callback: (String) -> Unit) {
        callBackStack["addFindPhoneListener"] = callback
    }

    fun clearSportDataListener(callback: (String) -> Unit) {
        callBackStack.remove("addSportDataListener")
    }

    fun clearFindPhoneListener(callback: (String) -> Unit) {
        callBackStack.remove("addFindPhoneListener")
    }

    fun syncTimeAndLanguage(request: String, callback: (String) -> Unit) {
        callBackStack["syncTimeAndLanguage"] = callback
        val bandTimeModel = gsonInstance.fromJson(request, BandTimeModel::class.java)
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_LANGUAGE,
                bandTimeModel.year.toByte(),
                bandTimeModel.month.toByte(),
                bandTimeModel.day.toByte(),
                bandTimeModel.hour.toByte(),
                bandTimeModel.minute.toByte(),
                bandTimeModel.second.toByte(),
                bandTimeModel.languageType.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun syncWeatherToBand(request: String, callback: (String) -> Unit) {
        callBackStack["syncWeatherToBand"] = callback
        val bandWeatherData = gsonInstance.fromJson(request, BandWeatherData::class.java)
        val sign = if (bandWeatherData.temperature < 0) 0 else 1
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_WEATHER,
                bandWeatherData.weatherType.toByte(),
                bandWeatherData.temperature.toByte(),
                sign.toByte(),
                bandWeatherData.temLow.toByte(),
                bandWeatherData.temHigh.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setBandAlarm(request: String, callback: (String) -> Unit) {
        callBackStack["setBandAlarm"] = callback
        val alarmData = gsonInstance.fromJson(request, BandAlarmData::class.java)
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_ALARM,
                alarmData.alarmHour.toByte(),
                alarmData.alarmMinute.toByte(),
                alarmData.isAlarmOn.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed setBandAlarm",
                null
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setBandHeartRateSetting(request: String, callback: (String) -> Unit) {
        callBackStack["setHeartRateSetting"] = callback
        val heartSettingData = gsonInstance.fromJson(request, BandHeartSettingData::class.java)
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_HEART_RATE,
                heartSettingData.heartRateInterval.toByte(),
                heartSettingData.isHeartRateOn.toByte(),
                heartSettingData.isHeartAlarmOn.toByte(),
                heartSettingData.heartRateUpperLimit.toByte(),
                heartSettingData.heartRateLowerLimit.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed setHeartRateSetting",
                null
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setOSPlatform(type: Int, callback: (String) -> Unit) {
        callBackStack["setOSPlatform"] = callback
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_OS_PLATFORM,
                type.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun findBand(callback: (String) -> Unit) {
        val ret = blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_FIND_BAND))
        val baseResponse = BaseResponse<String>(
            code = if (ret == true) 1 else 0,
            message = "Success",
            data = ret.toString()
        )
        callback(gsonInstance.toJson(baseResponse))
    }

    fun resetBand(callback: (String) -> Unit) {
        val ret = blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_FACTORY_SET))
        val baseResponse = BaseResponse<String>(
            code = if (ret == true) 1 else 0,
            message = if (ret == true) "Success" else "Failed resetBand",
            data = ret.toString()
        )
        callback(gsonInstance.toJson(baseResponse))
    }

    fun setScreenSaver(interval: Int, callback: (String) -> Unit) {
        callBackStack["setScreenSaver"] = callback
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_SCREEN_SAVER,
                interval.toByte()
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setGestureLightUp(enable: Boolean, callback: (String) -> Unit) {
        callBackStack["setGestureLightUp"] = callback
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_LIGHT_UP,
                if (enable) 1 else 0
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setAntiLost(enable: Boolean, callback: (String) -> Unit) {
        callBackStack["setAntiLost"] = callback
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_ANTI_LOST,
                if (enable) 1 else 0
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }

    fun setTimeFormat(format: Int, callback: (String) -> Unit) {
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_TIME_FORMAT,
                format.toByte()
            )
        )
        val baseResponse = BaseResponse<String>(
            code = if (ret == true) 1 else 0,
            message = "Success",
            data = ret.toString()
        )
        callback(gsonInstance.toJson(baseResponse))
    }

    fun setUserInfo(userInfo: String, callback: (String) -> Unit) {
        callBackStack["setUserInfo"] = callback
        val userModel = gsonInstance.fromJson(userInfo, BandUserInfo::class.java)
        val stepBuffer = ByteBuffer.allocate(4)
        stepBuffer.order(ByteOrder.LITTLE_ENDIAN)
        stepBuffer.putInt(userModel.targetStep)

        val data = byteArrayOf(
            BLEConstants.COMMAND_SET_USER_INFO,
            userModel.sexType.toByte(),
            userModel.age.toByte(),
            userModel.height.toByte(),
            userModel.weight.toByte(),
            stepBuffer.get(0),
            stepBuffer.get(1),
            stepBuffer.get(2),
            userModel.timeFormat.toByte()
        )
        val ret = blePeripheral?.writeJsonRequest(data)
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = "Failed"
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }


    private fun processUserInfo(userData: ByteArray) {
        val userInfo = BandUserInfo(
            userData[1].tinyInt(),
            userData[2].tinyInt(),
            userData[3].tinyInt(),
            userData[4].tinyInt(),
            userData[5].tinyInt()
                .or(userData[6].tinyInt().shl(8).or(userData[7].tinyInt().shl(16))),
            userData[8].tinyInt()
        )
        val baseResponse = BaseResponse<BandUserInfo>(
            code = 1,
            message = "Success",
            data = userInfo
        )
        callBackStack["getUserInfo"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun processSupportedFunc(jsonObject: ByteArray) {
        val flag = jsonObject[1].tinyInt()
        val flag1 = jsonObject[2].tinyInt()
        val supportFeatures = BandFeatures(
            flag.shr(3).and(0x01) == 1,
            flag.shr(5).and(0x01) == 1,
            flag.shr(6).and(0x01) == 1,
            flag.shr(7).and(0x01) == 1,
            flag1.shr(2).and(0x01) == 1
        )

        if (jsonObject.size > 4) {
            val p1 = jsonObject[3].tinyInt()
            val p2 = jsonObject[4].tinyInt()
            if (p1 != 0) {
                jlCustomSuffix = "WATCH${p1}"
            }
            if (p2 != 0) {
                jlDialMarketSuffix = "WATCH${p2}"
            }
        }

        watchThemeManager = WatchThemeManager(
            baseFileUrl,
            blePeripheral!!,
            isRoundWatch,
            isJLieWatch,
            jlCustomSuffix,
            jlDialMarketSuffix
        )

        val baseResponse = BaseResponse<BandFeatures>(
            code = 1,
            message = "Success: $jlCustomSuffix : $jlDialMarketSuffix",
            data = supportFeatures
        )
        callBackStack["getSupportedFeatures"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun processBasicConfig(jsonObject: ByteArray) {
        basicBandConfig = BandConfig(
            jsonObject[1].tinyInt(),
            jsonObject[2].tinyInt() == 1,
            jsonObject[3].tinyInt() == 1,
            jsonObject[4].tinyInt(),
            jsonObject[5].tinyInt(),
            jsonObject[6].tinyInt() == 1,
            jsonObject[7].tinyInt(),
            jsonObject[8].tinyInt(),
            jsonObject[9].tinyInt(),
            jsonObject[10].tinyInt() == 1,
            jsonObject[11].tinyInt().and(0xFFFF).or(jsonObject[12].tinyInt().and(0xFFFF).shl(8)),
            jsonObject[15].tinyInt()
        )
        val baseResponse = BaseResponse<BandConfig>(
            code = 1,
            message = "Success",
            data = basicBandConfig
        )
        callBackStack["getBasicConfig"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun processBandVersion(jsonObject: ByteArray) {
        if (jsonObject.size < 5) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Invalid band version response",
                data = null
            )
            callBackStack["readBandVersion"]?.let {
                it(gsonInstance.toJson(baseResponse))
            }
            return
        }
        val version = jsonObject.copyOfRange(4, 7)
            .joinToString(separator = ".") { it.tinyInt().toString() }
        val baseResponse = BaseResponse<BandVersion>(
            code = 1,
            message = "Success",
            data = BandVersion(
                jsonObject[2].tinyInt(),
                "V$version",
            )
        )
        callBackStack["readBandVersion"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    fun passJlAuth(){
        callBackStack["connectBle"]?.invoke(
            gsonInstance.toJson(
                BaseResponse<Any>(
                    1,
                    "Connect Success",
                    null
                )
            )
        )
        if (otaManager?.isOtaProcess == true) {
            otaManager?.changeToPreparedState()
        }
    }

    fun sendBandFailedSignal() {
        val baseResponse = BaseResponse<String>(
            code = 0,
            message = "Failed",
            data = "Get band config failed"
        )

        callBackStack["getBasicConfig"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun parseHeartRateData(data: ByteArray) {
        if (data.size < 6) return
        val health = HeartExamineData(
            data[1].tinyInt(),
            data[4].tinyInt(),
            data[3].tinyInt(),
            data[2].tinyInt(),
            data[5].tinyInt()
        )
        callBackStack["startHealthExamine"]?.let {
            it(gsonInstance.toJson(health))
        }
    }

    private fun parseDailyData(data: ByteArray) {
        val step = (data[2].toInt() and 0xFF).shl(8) or (data[3].toInt() and 0xFF)
        val day = SportDailyData(
            step,
            (data[4].toInt() and 0xFF),
            (data[5].toInt() and 0xFF),
            (data[6].toInt() and 0xFF),
            (data[7].toInt() and 0xFF),
            (data[8].toInt() and 0xFF)
        )
        val baseResponse = BaseResponse<SportDailyData>(
            code = 1,
            message = "Success",
            data = day
        )
        callBackStack["addSportDataListener"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun parseSleepDetail(data: ByteArray) {
        if (data.size < 11) {
            val baseResponse = BaseResponse<SportDailyData>(
                code = 0,
                message = "Receive wrong response",
                data = null
            )
            callBackStack["getSleepDetail"]?.let {
                it(gsonInstance.toJson(baseResponse))
            }
            return
        }
        val wakeTimes = data[10].tinyInt()
        val wakeList = mutableListOf<BandWakeDetail>()
        for (i in 11 until (11 + wakeTimes * 2) step 2) {
            if (i + 1 >= data.size) {
                break
            }
            wakeList.add(
                BandWakeDetail(
                    data[i].tinyInt(),
                    data[i + 1].tinyInt()
                )
            )
        }
        val sleepDetail = BandSleepDetail(
            data[1].tinyInt(),
            data[2].tinyInt(),
            data[3].tinyInt(),
            data[4].tinyInt(),
            data[5].tinyInt(),
            data[6].tinyInt(),
            data[7].tinyInt(),
            data[8].tinyInt(),
            data[9].tinyInt(),
            wakeTimes,
            wakeList
        )
//        val rowData = data.bytesToHex()
        val baseResponse = BaseResponse<BandSleepDetail>(
            code = 1,
            message = "Success",
            data = sleepDetail
        )
        callBackStack["getSleepDetail"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    private fun parseBatterySocData(data: ByteArray) {
        if (data.size < 2) {
            val baseResponse = BaseResponse<SportDailyData>(
                code = 0,
                message = "Receive wrong response",
                data = null
            )
            callBackStack["getBatterySoc"]?.let {
                it(gsonInstance.toJson(baseResponse))
            }
            return
        }
        val batterySoc = data[1].tinyInt()
        val baseResponse = BaseResponse<Int>(
            code = 1,
            message = "Success",
            data = batterySoc
        )
        callBackStack["getBatterySoc"]?.let {
            it(gsonInstance.toJson(baseResponse))
        }
    }

    fun addNewWatchTheme(filePath: String, progress: (Int) -> Unit, callback: (String) -> Unit) {
        if (watchThemeManager == null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Watch theme manager not initialized",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        watchThemeManager?.addNewWatchTheme(
            filePath,
            progress,
            callback
        )
    }

    fun addWatchBackground(request: String, progress: (Int) -> Unit, callback: (String) -> Unit) {
        if (watchThemeManager == null) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Watch theme manager not initialized",
                data = null
            )
            callback(
                gsonInstance.toJson(baseResponse)
            )
            return
        }
        val customThemeData = gsonInstance.fromJson(request, CustomThemeData::class.java)
        watchThemeManager?.addWatchBackground(
            customThemeData,
            progress,
            callback
        )
    }

    fun startOta(binPath: String, callback: (Int, Int, String) -> Unit) {
        if (binPath.isEmpty()) {
            callback(0, 0, "文件为空")
            return
        }
        progressCallback = callback
        otaManager!!.bluetoothOption.firmwareFilePath = binPath
        MainScope().launch {
            delay(500)
            startOtaProcess()
        }
    }

    private fun reconnectProcess() {
        scanForTargetDevice(otaReconnectAddress) {
            selectedDevice = it
            connectWithMac(mContext!!)
            otaManager?.setUpBlePeripheral(blePeripheral!!)
        }
    }

    private fun startOtaProcess() {
        if (otaManager == null || blePeripheral == null) {
            progressCallback?.invoke(2, 0, "蓝牙未初始化")
            return
        }
        otaManager!!.startOTA(object : IUpgradeCallback {
            override fun onStartOTA() {
                otaManager?.isOtaProcess = true
            }

            override fun onNeedReconnect(p0: String?, p1: Boolean) {
                otaReconnectAddress = p0 ?: ""
                reconnectProcess()
            }

            override fun onProgress(p0: Int, p1: Float) {
                progressCallback?.invoke(0x0b, p1.toInt(), "")
            }

            override fun onStopOTA() {
                otaManager?.isOtaProcess = false
                progressCallback?.invoke(1, 100, "OTA完成")
            }

            override fun onCancelOTA() {}

            override fun onError(p0: BaseError?) {
                otaManager?.isOtaProcess = false
                p0?.let {
                    progressCallback?.invoke(0, 0, it.message)
                }

            }
        })
    }


    private fun addTwoArray(btX: ByteArray, btY: ByteArray): ByteArray {
        val btZ = ByteArray(btX.size + btY.size)
        System.arraycopy(btX, 0, btZ, 0, btX.size)
        System.arraycopy(btY, 0, btZ, btX.size, btY.size)
        return btZ
    }

    fun sendNotification(request: String) {
        val notify = gsonInstance.fromJson(request, WatchNotifyData::class.java)
        if (isJLieWatch) {
            JLWatchManager.shared.sendNotifications(
                "",
                notify.title,
                notify.content,
                notify.type
            )
        } else {
            val dataByte = "${notify.title}:${notify.content}".toByteArray(StandardCharsets.UTF_8)
            if (dataByte.size < 17) {
                val head = byteArrayOf(0x21, notify.type.toByte(), 0x01)
                blePeripheral?.writeJsonRequest(addTwoArray(head, dataByte))
            } else {
                var total = 1 + ceil((dataByte.size - 17) / 19.0).toInt()
                total = total.coerceAtMost(17)
                val head = byteArrayOf(0x21, notify.type.toByte(), total.toByte())
                val subHead = dataByte.copyOfRange(0, 17)
                blePeripheral?.writeJsonRequest(head + subHead)
                // rest part
                val dataLength = dataByte.size
                var startIndex = 17
                while (startIndex < dataLength) {
                    var endIndex = startIndex + 19
                    if (endIndex > dataLength) {
                        endIndex = dataLength
                    }
                    val cmdSub = dataByte.copyOfRange(startIndex, endIndex)
                    blePeripheral?.writeJsonRequest(byteArrayOf(0x21, *cmdSub))
                    startIndex = endIndex
                }
            }
        }
    }

    fun clearCallStatus() {
        blePeripheral?.writeJsonRequest(byteArrayOf(BLEConstants.COMMAND_OFF_CALL))
    }


    fun addCallStatusListener(callback: (String) -> Unit) {
        callBackStack["addCallStatusListener"] = callback
    }

    fun addShakeCameraOpenListener(callback: (String) -> Unit) {
        callBackStack["addShakeCameraOpenListener"] = callback
    }

    fun addShakeCameraListener(callback: (String) -> Unit) {
        callBackStack["addShakeCameraListener"] = callback
    }

    fun setShakeCameraStatus(enable: Boolean, callback: (String) -> Unit) {
        callBackStack["setShakeCameraStatus"] = callback
        val ret = blePeripheral?.writeJsonRequest(
            byteArrayOf(
                BLEConstants.COMMAND_CAMERA_TO_BAND,
                if (enable) 1 else 0
            )
        )
        if (ret != true) {
            val baseResponse = BaseResponse<String>(
                code = 0,
                message = "Failed",
                data = ret.toString()
            )
            callback(gsonInstance.toJson(baseResponse))
        }
    }


}